home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / CWTPU11 / CWARE.PAS < prev    next >
Pascal/Delphi Source File  |  1989-11-07  |  25KB  |  729 lines

  1. {$A+,B-,D+,E-,F+,I-,L+,N-,O+,R-,S+,V-}
  2.  
  3. Unit CWare;
  4.  
  5. (* Version 1.1 - CollisionWare Premium SoftWare - Compiled by Kito Mann *)
  6. (* This unit is a simple collection of some some procedures aquired     *)
  7. (* from other programs and myself. New versions will have added         *)
  8. (* procedures, and the present ones will be improved. Comments, bugs,   *)
  9. (* and questions accepted.                                              *)
  10. (* Keep in mind that there is NO WARANTY! It IS NOT GAURANTEED that all *)
  11. (* these procedures will work!                                          *)
  12. (* If you modify the procedures included, or add your own, I request    *)
  13. (* that you send me a copy of the new unit and source code.             *)
  14.  
  15. (* It'd probably be helpful if you declare ErrorCode: byte in your main *)
  16. (* program. It is used as an Error variable much like the DosError used *)
  17. (* in the DOS unit.                                                     *)
  18.  
  19. (* The Collision Theory pm-BBS *)
  20. (*         24 hours            *)
  21. (*      (703)503-9441          * <--- NUMBER AND HOUR CHANGE! *)
  22. (*         Burke, VA           *)
  23. (* "Dedicated to Intelligent   *)
  24. (*        Conversation"        *)
  25.  
  26. INTERFACE
  27.  
  28. Uses Crt,
  29.      Dos;
  30.  
  31. const
  32.   MaxDirEnteries=       20;    { Maximum number of directories that can be specified to search }
  33.                                { This doesn't include those searched "below" ones specified.   }
  34.  
  35. type
  36.   FullNameStr=          string[12];                 { Type for storing name+dot+extention                                 }
  37.   DirSearchEntry=       record                      { This data type is used to store all the paths that will be searched }
  38.                           Dir:         DirStr;      {   <-- Path to search                                                }
  39.                           Name:        FullNameStr; {   <-- File spec to search                                           }
  40.                           Below:       boolean;     {   <-- TRUE=search directories below the specified one               }
  41.                         end;
  42.   ProcType=             procedure(var S: SearchRec; P: PathStr);
  43.   AnyStr=               string[255];
  44.  
  45.  
  46. var
  47.   EngineMask:           FullNameStr;
  48.   EngineAttr:           byte;
  49.   EngineProc:           ProcType;
  50.   EngineCode:           byte;
  51.  
  52.   Reg:                  Registers;   { Register storage for DOS calls }
  53.   OldSeg,OldOfs:        word;
  54.   BufData:              longint;
  55.   BufferSeg:            word;
  56.   BufferOfs:            word;
  57.   BufferLen:            word;
  58.   BufferPtr:            pointer;
  59.   T:                    text;
  60.   P:                    PathStr;
  61.  
  62.  
  63. (* File and Keyboard Buffer procedures *)
  64.  
  65. function FileFound(F: ComStr): boolean;
  66.  
  67. procedure SearchEngine(Mask: PathStr; Attr: byte; Proc: ProcType; var ErrorCode: byte);
  68.  
  69. function GoodDirectory(S: SearchRec): boolean;
  70.  
  71. procedure SearchOneDir(var S: SearchRec; P: PathStr);
  72.  
  73. procedure SearchEngineAll(Path: PathStr; Mask: FullNameStr; Attr: byte;
  74.                           Proc: ProcType; var ErrorCode: byte);
  75.  
  76. procedure IPP;
  77.  
  78. procedure NewExitProc2;
  79.  
  80. procedure ResetBuffer;
  81.  
  82. function BufSize: word;
  83.  
  84. function InBuffer(S: string): integer;
  85.  
  86. procedure InstallInterruptHandler;
  87.  
  88. procedure DeleteFiles(P: string);
  89.  
  90. procedure DeleteDir(P:string);
  91.  
  92. procedure ListFiles(P: string; complete:boolean; pausenum:integer);
  93.  
  94. (* Misc. String procedures *)
  95.  
  96. function DateString: string;
  97.  
  98. function TimeString: string;
  99.  
  100. procedure Tab(s1,s2:AnyStr; i:integer);
  101.  
  102. Function UpCaseString(StrIn : String) : String;
  103. { Convert a string to upper case }
  104.  
  105. Function PathOnly(FileName : String) : String;
  106. { Strip any filename information from a file specification }
  107.  
  108. Function NameOnly(FileName : String) : String;
  109. { Strip any path information from a file specification }
  110.  
  111. Function BaseNameOnly(FileName : String) : String;
  112. { Strip any path and extension information from a file specification }
  113.  
  114. Function ExtOnly(FileName : String) : String;
  115. { Return only the extension portion of a filename }
  116.  
  117. Function IntStr(Int : LongInt; Form : Integer) : String;
  118. { Convert an Integer variable to a string }
  119.  
  120. Function Strr(Int:LongInt) : String;
  121. { Same as IntStr but does not use the variable "Form" }
  122.  
  123. Function SameFile(File1, File2 : String) : Boolean;
  124. { Call to find out if File1 has a name equivalent to File2.  Both filespecs }
  125. { may contain wildcards.                                                    }
  126.  
  127.  
  128. IMPLEMENTATION
  129.  
  130. { -------------------------------------------------------------------------- }
  131.  
  132. function FileFound(F: ComStr): boolean;
  133. {
  134.   This returns TRUE if the file F exists, FALSE otherwise.  F can contain
  135.   wildcard characters.
  136. }
  137. var
  138.   SRec:                 SearchRec;
  139. begin
  140.   SRec.Name := '*';
  141.   FindFirst(F,0,SRec);
  142.   if SRec.Name='*' then FileFound := false else FileFound := true;
  143. end;
  144.  
  145.  
  146. (********* The following search engine routines are sneakly swiped *********)
  147. (********* from Turbo Technix v1n6.  See there for further details *********)
  148.  
  149. procedure SearchEngine(Mask: PathStr; Attr: byte; Proc: ProcType;
  150.                        var ErrorCode: byte);
  151. var
  152.   S:                    SearchRec;
  153.   P:                    PathStr;
  154.   Ext:                  ExtStr;
  155. begin
  156.   FSplit(Mask, P, Mask, Ext);
  157.   Mask := Mask+Ext;
  158.   FindFirst(P+Mask,Attr,S);
  159.   if DosError<>0 then
  160.   begin
  161.     ErrorCode := DosError;
  162.     exit;
  163.   end;
  164.   while DosError=0 do
  165.   begin
  166.     Proc(S, P);
  167.     FindNext(S);
  168.   end;
  169.   if DosError=18 then ErrorCode := 0
  170.   else ErrorCode := DosError;
  171. end;
  172.  
  173. { -------------------------------------------------------------------------- }
  174.  
  175. function GoodDirectory(S: SearchRec): boolean;
  176. begin
  177.   GoodDirectory := (S.name<>'.') and (S.Name<>'..') and
  178.   (S.Attr and Directory=Directory);
  179. end;
  180.  
  181. { -------------------------------------------------------------------------- }
  182.  
  183. procedure SearchOneDir(var S: SearchRec; P: PathStr);
  184. begin
  185.   if GoodDirectory(S) then
  186.   begin
  187.     P := P+S.Name;
  188.     SearchEngine(P+'\'+EngineMask,EngineAttr,EngineProc,EngineCode);
  189.     SearchEngine(P+'\*.*',Directory or Archive, SearchOneDir,EngineCode);
  190.   end;
  191. end;
  192.  
  193. { -------------------------------------------------------------------------- }
  194.  
  195. procedure SearchEngineAll(Path: PathStr; Mask: FullNameStr; Attr: byte;
  196.                           Proc: ProcType; var ErrorCode: byte);
  197. begin
  198.   EngineMask := Mask;
  199.   EngineProc := Proc;
  200.   EngineAttr := Attr;
  201.   SearchEngine(Path+Mask,Attr,Proc,ErrorCode);
  202.   SearchEngine(Path+'*.*',Directory or Archive,SearchOneDir,ErrorCode);
  203.   ErrorCode := EngineCode;
  204. end;
  205.  
  206. (************** Thus ends the sneakly swiped code *************)
  207.  
  208. { -------------------------------------------------------------------------- }
  209.  
  210. procedure IPP;
  211. { Interrupt pre-processor.  This is a new handler for interrupt 29h which
  212.   provides special functions.  See comments in IHAND.ASM}
  213. begin
  214.   InLine(
  215.       $06/                   {          push    es                      }
  216.       $1E/                   {          push    ds                      }
  217.       $53/                   {          push    bx                      }
  218.       $57/                   {          push    di                      }
  219.       $BB/$3F/$3F/           {          mov     bx, 3f3fh               }
  220.       $8E/$C3/               {          mov     es, bx                  }
  221.       $BB/$3F/$3F/           {          mov     bx, 3f3fh               }
  222.       $26/$8B/$3F/           {          mov     di, word ptr [es:bx]    }
  223.       $26/$8E/$5F/$02/       {          mov     ds, word ptr [es:bx+2]  }
  224.       $88/$05/               {          mov     byte ptr [di], al       }
  225.       $26/$FF/$07/           {          inc     word ptr [es:bx]        }
  226.       $5F/                   {          pop     di                      }
  227.       $5B/                   {          pop     bx                      }
  228.       $1F/                   {          pop     ds                      }
  229.       $07/                   {          pop     es                      }
  230.       $3C/$0A/               {          cmp     al, 10                  }
  231.       $75/$28/               {          jne     looper                  }
  232.       $50/                   {          push    ax                      }
  233.       $52/                   {          push    dx                      }
  234.       $51/                   {          push    cx                      }
  235.       $53/                   {          push    bx                      }
  236.       $B4/$03/               {          mov     ah, 3                   }
  237.       $B7/$00/               {          mov     bh, 0                   }
  238.       $CD/$10/               {          int     10h                     }
  239.       $80/$FE/$18/           {          cmp     dh, 24                  }
  240.       $75/$15/               {          jne     popper                  }
  241.       $FE/$CE/               {          dec     dh                      }
  242.       $B7/$00/               {          mov     bh, 0                   }
  243.       $B4/$02/               {          mov     ah, 2                   }
  244.       $CD/$10/               {          int     10h                     }
  245.       $B8/$01/$06/           {          mov     ax, 0601h               }
  246.       $B7/$07/               {          mov     bh, 7                   }
  247.       $B9/$00/$11/           {          mov     cx, 1100h               }
  248.       $BA/$4F/$18/           {          mov     dx, 184fh               }
  249.       $CD/$10/               {          int     10h                     }
  250.       $5B/                   {  popper: pop     bx                      }
  251.       $59/                   {          pop     cx                      }
  252.       $5A/                   {          pop     dx                      }
  253.       $58/                   {          pop     ax                      }
  254.       $9C/                   {  looper: pushf                           }
  255.       $9A/$00/$00/$00/$00/   {          call    far [0:0]               }
  256.       $CF);                  {          iret                            }
  257. end;
  258.  
  259. { -------------------------------------------------------------------------- }
  260.  
  261. procedure NewExitProc2;
  262. { This exit procedure removes the interrupt 29h handler from memory and places
  263.   the cursor at the bottom of the screen. }
  264. begin
  265.   Reg.AH := $25;
  266.   Reg.AL := $29;
  267.   Reg.DS := OldSeg;
  268.   Reg.DX := OldOfs;
  269.   MsDos(Reg);
  270.   Window(1,1,80,25);
  271.   GotoXY(1,24);
  272.   TextAttr := $07;
  273.   ClrEol;
  274. end;
  275.  
  276. { -------------------------------------------------------------------------- }
  277.  
  278. procedure ResetBuffer;
  279. { Reset pointers to the text buffer, effectivly deleting any text in it }
  280. begin
  281.   MemW[seg(BufData):ofs(BufData)] := BufferOfs;    { Set first 2 bytes of BufData to point to buffer offset }
  282.   MemW[seg(BufData):ofs(BufData)+2] := BufferSeg;  { And next two bytes to point to buffer segment }
  283.   MemW[seg(IPP):ofs(IPP)+21] := seg(BufData);    { Now point the interrupt routine to BufData for pointer }
  284.   MemW[seg(IPP):ofs(IPP)+26] := ofs(BufData);    {  to the text buffer }
  285. end;
  286.  
  287. { -------------------------------------------------------------------------- }
  288.  
  289. function BufSize: word;
  290. { This returns the number of characters in the text buffer.  It's what BufData
  291.   now points to minus what is origionally pointed to, eg, the number of times
  292.   IPP incremented it }
  293. begin
  294.   BufSize := MemW[seg(BufData):ofs(BufData)]-BufferOfs;
  295. end;
  296.  
  297. { -------------------------------------------------------------------------- }
  298.  
  299. function InBuffer(S: string): integer;
  300. { This searched the text buffer for the string S, and if it's found returns
  301.   the offset in the buffer.  If it's not found a -1 is returned }
  302. var
  303.   L,M:                  word;
  304.   X:                    byte;
  305. begin
  306.   X := 1;
  307.   L := BufferOfs;
  308.   M := BufSize;
  309.   while (X<=length(S)) and (L<=M) do
  310.   begin
  311.     if Mem[BufferSeg:L]=byte(S[X]) then Inc(X) else X := 1;
  312.     Inc(L);
  313.   end;
  314.   if X>length(S) then InBuffer := L-length(S) else InBuffer := -1;
  315. end;
  316.  
  317. { -------------------------------------------------------------------------- }
  318.  
  319. procedure InstallInterruptHandler;
  320. { Installs the int 29h handler }
  321. begin
  322.   BufferLen := $4000;  { Set up a 16k buffer }
  323.   GetMem(BufferPtr,BufferLen);  { Allocate memory pointed at by BufferPtr }
  324.   BufferSeg := seg(BufferPtr^);  { Read segment and offset of buffer for easy access }
  325.   BufferOfs := ofs(BufferPtr^);
  326.   ResetBuffer;    { Place these values in the IPP routine, resetting buffer }
  327.   Reg.AH := $35;
  328.   Reg.AL := $29;  { DOS service 35h, get interrupt vector for 29h }
  329.   MsDos(Reg);
  330.   OldSeg := Reg.ES;   { Store the segment and offset of the old vector for later use }
  331.   OldOfs := Reg.BX;
  332.   MemW[seg(IPP):ofs(IPP)+90] := Reg.BX;  { And store them so IPP can call the routine }
  333.   MemW[seg(IPP):ofs(IPP)+92] := Reg.ES;
  334.   Reg.AL := $29; { DOS service 25h, set interrupt vector 29h }
  335.   Reg.AH := $25;
  336.   Reg.DS := seg(IPP);    { Store segment and offset for IPP.  The +16 is to skip TP stack }
  337.   Reg.DX := ofs(IPP)+16; { maintainence routines }
  338.   MsDos(Reg);
  339. end;
  340.  
  341. { -------------------------------------------------------------------------- }
  342.  
  343.   procedure DeleteFiles(P: string);
  344.   {
  345.     Delete all files in the directory named, including
  346.     Hidden, Read-only, System and other file types.
  347.   }
  348.   var
  349.     SRec:               SearchRec;
  350.     ErrorCode:          byte;
  351.   begin
  352.     FindFirst(P+'\*.*',0,SRec);
  353.     while DosError=0 do
  354.     begin
  355.       Assign(T, P+'\'+SRec.Name);
  356.       SetFAttr(T,Archive);
  357.       writeln('Deleting ',P,+'\'+Srec.Name);
  358.       {$I-}
  359.       Erase(T);
  360.       {$I+}
  361.       ErrorCode := IOResult;
  362.       FindNext(SRec);
  363.     end;
  364.     ErrorCode := IOResult;
  365. end;
  366.  
  367. { -------------------------------------------------------------------------- }
  368.  
  369. procedure DeleteDir(P:string);
  370.  
  371. { Simply deletes specified directory }
  372.  
  373. var ErrorCode: byte;
  374. begin
  375.   DeleteFiles(P);
  376.   {$I-}
  377.   RmDir(P);
  378.   {$I+}
  379.   ErrorCode := IOResult;
  380. end;
  381.  
  382. { -------------------------------------------------------------------------- }
  383.  
  384. procedure ListFiles(P: string; complete:boolean; pausenum:integer);
  385.   {
  386.    If complete is true then will show the name and file size of every
  387.    file. Otherwise will just show the filename. Numlines is the number
  388.    of files it will display before a pause. 0 means no pause.
  389.   }
  390.   var
  391.     SRec:               SearchRec;
  392.     ErrorCode:          byte;
  393.     Size:               AnyStr;
  394.     Index:              integer;
  395.     TheChar:            char;
  396.     Quit:               boolean;
  397.  
  398.   begin
  399.     Quit:=false;
  400.     FindFirst(P+'\*.*',0,SRec);
  401.     Index:=1;
  402.     while DosError=0 do
  403.     begin
  404.        if Index=pausenum then 
  405.        begin
  406.         write('[Q=quit, ANY KEY=continue]:');
  407.         TheChar:=UpCase(ReadKey); writeln(TheChar);
  408.         if TheChar='Q' then quit:=true;
  409.         writeln;
  410.         Index:=0;
  411.        end;
  412.       if NOT Quit then 
  413.       if complete then begin
  414.         Size:=strr(Srec.Size);
  415.         tab(Srec.Name,Size,15);
  416.         writeln;
  417.       end else
  418.       writeln(Srec.Name);
  419.       FindNext(SRec);
  420.       Inc(Index);
  421.     end;
  422.     ErrorCode := IOResult;
  423. end;
  424.  
  425. { -------------------------------------------------------------------------- }
  426.  
  427.   function DateString: string;
  428.   {
  429.     Returns the current date in a string of the form:  MON ## YEAR.
  430.     E.g, 21 Feb 1989 or 02 Jan 1988.
  431.   }
  432.   const
  433.     Month:              array[1..12] of string[3]=
  434.                         ('Jan','Feb','Mar','Apr','May','Jun',
  435.                          'Jul','Aug','Sep','Oct','Nov','Dec');
  436.   var
  437.     Y,M,D,Junk:         word;
  438.     DS,YS:              string[5];
  439.   begin
  440.     GetDate(Y,M,D,Junk);
  441.     Str(Y,YS);
  442.     Str(D,DS);
  443.     if length(DS)<2 then DS := '0'+DS;
  444.     DateString := DS+' '+Month[M]+' '+YS;
  445.   end;
  446.  
  447. { -------------------------------------------------------------------------- }
  448.  
  449.   function TimeString: string;
  450.   {
  451.     Returns the current time in the form:  HH:MM am/pm
  452.     E.g, 12:00 am or 09:12 pm.
  453.   }
  454.   var
  455.     H,M,Junk:           word;
  456.     HS,MS:              string[5];
  457.     Am:                 boolean;
  458.   begin
  459.     GetTime(H,M,Junk,Junk);
  460.     case H of
  461.       0:     begin
  462.                Am := true;
  463.                H := 12;
  464.              end;
  465.       1..11: Am := true;
  466.       12:    Am := false;
  467.       else   begin
  468.                Am := false;
  469.                H := H-12;
  470.              end;
  471.     end;
  472.     Str(H,HS);
  473.     Str(M,MS);
  474.     if length(HS)<2 then HS := '0'+HS;
  475.     if length(MS)<2 then MS := '0'+MS;
  476.     if Am then TimeString := HS+':'+MS+' am'
  477.     else TimeString := HS+':'+MS+' pm';
  478.   end;
  479.  
  480. { -------------------------------------------------------------------------- }
  481.  
  482. procedure Tab(s1,s2:AnyStr; i:integer);
  483.  
  484. { Writes s1, then goes to i-length(s1) and writes s2 }
  485.  
  486. var j,k:integer;
  487. begin
  488.   j:=length(s1);
  489.   i:=i-j;
  490.   write(s1);
  491.   for k:=1 to i do write(' ');
  492.   write(s2);
  493. end;
  494.  
  495. { -------------------------------------------------------------------------- }
  496.  
  497. Function UpCaseString(StrIn : String) : String;
  498. Begin
  499.    Inline(                   { Thanks to Phil Burns for this routine }
  500.  
  501.       $1E/                   {         PUSH    DS                ; Save DS}
  502.       $C5/$76/$06/           {         LDS     SI,[BP+6]         ; Get source string address}
  503.       $C4/$7E/$0A/           {         LES     DI,[BP+10]        ; Get result string address}
  504.       $FC/                   {         CLD                       ; Forward direction for strings}
  505.       $AC/                   {         LODSB                     ; Get length of source string}
  506.       $AA/                   {         STOSB                     ; Copy to result string}
  507.       $30/$ED/               {         XOR     CH,CH}
  508.       $88/$C1/               {         MOV     CL,AL             ; Move string length to CL}
  509.       $E3/$0E/               {         JCXZ    Exit              ; Skip if null string}
  510.                              {;}
  511.       $AC/                   {UpCase1: LODSB                     ; Get next source character}
  512.       $3C/$61/               {         CMP     AL,'a'            ; Check if lower-case letter}
  513.       $72/$06/               {         JB      UpCase2}
  514.       $3C/$7A/               {         CMP     AL,'z'}
  515.       $77/$02/               {         JA      UpCase2}
  516.       $2C/$20/               {         SUB     AL,'a'-'A'        ; Convert to uppercase}
  517.                              {;}
  518.       $AA/                   {UpCase2: STOSB                     ; Store in result}
  519.       $E2/$F2/               {         LOOP    UpCase1}
  520.                              {;}
  521.       $1F);                  {Exit:    POP     DS                ; Restore DS}
  522.  
  523. end {UpCaseString};
  524.  
  525. { -------------------------------------------------------------------------- }
  526.  
  527. Function PathOnly(FileName : String) : String;
  528. Var
  529.    Dir  : DirStr;
  530.    Name : NameStr;
  531.    Ext  : ExtStr;
  532. Begin
  533.    FSplit(FileName, Dir, Name, Ext);
  534.    PathOnly := Dir;
  535. End {PathOnly};
  536.  
  537. { --------------------------------------------------------------------------- }
  538.  
  539. Function NameOnly(FileName : String) : String;
  540. { Strip any path information from a file specification }
  541. Var
  542.    Dir  : DirStr;
  543.    Name : NameStr;
  544.    Ext  : ExtStr;
  545. Begin
  546.    FSplit(FileName, Dir, Name, Ext);
  547.    NameOnly := Name + Ext;
  548. End {NameOnly};
  549.  
  550. { --------------------------------------------------------------------------- }
  551.  
  552. Function BaseNameOnly(FileName : String) : String;
  553. { Strip any path and extension from a file specification }
  554. Var
  555.    Dir  : DirStr;
  556.    Name : NameStr;
  557.    Ext  : ExtStr;
  558. Begin
  559.    FSplit(FileName, Dir, Name, Ext);
  560.    BaseNameOnly := Name;
  561. End {BaseNameOnly};
  562.  
  563. { --------------------------------------------------------------------------- }
  564.  
  565. Function ExtOnly(FileName : String) : String;
  566. { Strip the path and name from a file specification.  Return only the }
  567. { filename extension.                                                 }
  568. Var
  569.    Dir  : DirStr;
  570.    Name : NameStr;
  571.    Ext  : ExtStr;
  572. Begin
  573.    FSplit(FileName, Dir, Name, Ext);
  574.    If Pos('.', Ext) <> 0 then
  575.       Delete(Ext, 1, 1);
  576.    ExtOnly := Ext;
  577. End {ExtOnly};
  578.  
  579. { --------------------------------------------------------------------------- }
  580.  
  581. Function IntStr(Int : LongInt; Form : Integer) : String;
  582. Var
  583.    S : String;
  584. Begin
  585.    If Form = 0 then
  586.       Str(Int, S)
  587.    else
  588.       Str(Int:Form, S);
  589.    IntStr := S;
  590. End {IntStr};
  591.  
  592. { --------------------------------------------------------------------------- }
  593.  
  594. Function Strr(Int : LongInt) : String; { Added for my own sake - KM }
  595. Var
  596.    S : String;
  597. Begin
  598.    Str(Int, S);
  599.    Strr := S;
  600. End {Strr};
  601.  
  602. { --------------------------------------------------------------------------- }
  603.  
  604. Function SameName(N1, N2 : String) : Boolean;
  605. {
  606.   Function to compare filespecs.
  607.  
  608.   Wildcards allowed in either name.
  609.   Filenames should be compared seperately from filename extensions by using
  610.      seperate calls to this function
  611.         e.g.  FName1.Ex1
  612.               FName2.Ex2
  613.               are they the same?
  614.               they are if SameName(FName1, FName2) AND SameName(Ex1, Ex2)
  615.  
  616.   Wildcards work the way DOS should've let them work (eg. *XX.DAT doesn't
  617.   match just any file...only those with 'XX' as the last two characters of
  618.   the name portion and 'DAT' as the extension).
  619.  
  620.   This routine calls itself recursively to resolve wildcard matches.
  621.  
  622. }
  623. Var
  624.    P1, P2 : Integer;
  625.    Match  : Boolean;
  626. Begin
  627.    P1    := 1;
  628.    P2    := 1;
  629.    Match := TRUE;
  630.  
  631.    If (Length(N1) = 0) and (Length(N2) = 0) then
  632.       Match := True
  633.    else
  634.       If Length(N1) = 0 then
  635.          If N2[1] = '*' then
  636.             Match := TRUE
  637.          else
  638.             Match := FALSE
  639.       else
  640.          If Length(N2) = 0 then
  641.             If N1[1] = '*' then
  642.                Match := TRUE
  643.             else
  644.                Match := FALSE;
  645.  
  646.    While (Match = TRUE) and (P1 <= Length(N1)) and (P2 <= Length(N2)) do
  647.       If (N1[P1] = '?') or (N2[P2] = '?') then begin
  648.          Inc(P1);
  649.          Inc(P2);
  650.       end {then}
  651.       else
  652.          If N1[P1] = '*' then begin
  653.             Inc(P1);
  654.             If P1 <= Length(N1) then begin
  655.                While (P2 <= Length(N2)) and Not SameName(Copy(N1,P1,Length(N1)-P1+1), Copy(N2,P2,Length(N2)-P2+1)) do
  656.                   Inc(P2);
  657.                If P2 > Length(N2) then
  658.                   Match := FALSE
  659.                else begin
  660.                   P1 := Succ(Length(N1));
  661.                   P2 := Succ(Length(N2));
  662.                end {if};
  663.             end {then}
  664.             else
  665.                P2 := Succ(Length(N2));
  666.          end {then}
  667.          else
  668.             If N2[P2] = '*' then begin
  669.                Inc(P2);
  670.                If P2 <= Length(N2) then begin
  671.                   While (P1 <= Length(N1)) and Not SameName(Copy(N1,P1,Length(N1)-P1+1), Copy(N2,P2,Length(N2)-P2+1)) do
  672.                      Inc(P1);
  673.                   If P1 > Length(N1) then
  674.                      Match := FALSE
  675.                   else begin
  676.                      P1 := Succ(Length(N1));
  677.                      P2 := Succ(Length(N2));
  678.                   end {if};
  679.                end {then}
  680.                else
  681.                   P1 := Succ(Length(N1));
  682.             end {then}
  683.             else
  684.                If UpCase(N1[P1]) = UpCase(N2[P2]) then begin
  685.                   Inc(P1);
  686.                   Inc(P2);
  687.                end {then}
  688.                else
  689.                   Match := FALSE;
  690.  
  691.    If P1 > Length(N1) then begin
  692.       While (P2 <= Length(N2)) and (N2[P2] = '*') do
  693.          Inc(P2);
  694.       If P2 <= Length(N2) then
  695.          Match := FALSE;
  696.    end {if};
  697.  
  698.    If P2 > Length(N2) then begin
  699.       While (P1 <= Length(N1)) and (N1[P1] = '*') do
  700.          Inc(P1);
  701.       If P1 <= Length(N1) then
  702.          Match := FALSE;
  703.    end {if};
  704.  
  705.    SameName := Match;
  706.  
  707. End {SameName};
  708.  
  709. { ---------------------------------------------------------------------------- }
  710.  
  711. Function SameFile(File1, File2 : String) : Boolean;
  712. Var
  713.    Path1, Path2 : String;
  714. Begin
  715.  
  716.    File1 := FExpand(File1);
  717.    File2 := FExpand(File2);
  718.    Path1 := PathOnly(File1);
  719.    Path2 := PathOnly(File2);
  720.  
  721.    SameFile := SameName(BaseNameOnly(File1), BaseNameOnly(File2)) AND
  722.                SameName(ExtOnly(File1), ExtOnly(File2))           AND
  723.                (Path1 = Path2);
  724.  
  725. End {SameFile};
  726.  
  727. { ---------------------------------------------------------------------------- }
  728.  
  729. End {Unit CWARE}.